/*
 * Copyright (c) 2009-2013 EvTech Project
 * All rights reserved.
 * 
 * This file is part of Symbol Matching.
 *
 * Symbol Matching is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or 
 * (at your option) any later version.
 *
 * Symbol Matching is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Symbol Matching.  If not, see <http://www.gnu.org/licenses/>.
 * 
 */
package fi.honkalampisaatio.reaktiopeli.game;


import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

import javax.sound.sampled.Clip;

import fi.honkalampisaatio.game.logging.GameLogger;
import fi.honkalampisaatio.game.utilities.MediaLoader;
import fi.honkalampisaatio.reaktiopeli.target.Target;
import fi.honkalampisaatio.reaktiopeli.target.TargetEvent;
import fi.honkalampisaatio.reaktiopeli.target.TargetEventListener;
import fi.honkalampisaatio.reaktiopeli.xml.GameTime;

public class Player implements KeyListener {
	public enum STATE{READY,FULL_HIT,FAIL};
	protected STATE state;
	protected String playerName;
	protected int score;
	protected boolean isOmitMistakes;
	protected ArrayList<String> cs;
	private BufferedImage playerImg;
	protected Set<Integer> pressedButtonId;
	private static final int MAX_IMAGE_HEIGHT = 300;

	private static final int MAX_IMAGE_WIDTH = 300;
	
	private TargetEventListener listener = null;
	// Joukko, jossa kaikki nyt näytettävät osumattomat targetit
	private Set<Target> currentNonHitTargets;
	// Joukko, jossa kaikki nyt näytettävät ja osutut targetit
	private Set<Target> currentHittedTargets;
	
	protected MidiSounds midiSounds;
	private Clip pushClip;
	
	private GamePanel gp = null;
	
	private GameTime gameTime;
	private String folder;
	private long currentRoundTime, maxRoundTime;
	
	public Player(MidiSounds midiSounds, GamePanel gp) {
		score = 0;
		state = STATE.READY;
		this.midiSounds = midiSounds;
		this.gp = gp;
		gameTime = new GameTime();
		isOmitMistakes = true;
		currentNonHitTargets = new HashSet<Target>();
		currentHittedTargets = new HashSet<Target>();
		pressedButtonId = new HashSet<Integer>();
		pushClip = MediaLoader.createSoundClip("./aanet/chimes.wav");
		currentRoundTime = 0;
		maxRoundTime = 0;
	}

	public void setOmitMistakes(boolean omitMistakes)
	{
		this.isOmitMistakes = omitMistakes;
	}
	public void setControls(ArrayList<String> controls) {
		this.cs = controls;
	}

	public String getPlayerName() {
		return playerName;
	}
	
	public void setPlayerName(String name) {
		this.playerName = name;
	}
	
	public void setScore(int score) {
		this.score = score;
	}

	public int getScore() {
		return score;
	}
	
	public Set<Integer> getPressedButtonIds() {
		return pressedButtonId;
	}


	@Override
	public void keyPressed(KeyEvent e) {
	//	if(this.state!=STATE.READY || cs==null) {
		//	return;
	//	}
		String keyCode = KeyEvent.getKeyText(e.getKeyCode());
		for(int i=0; i<cs.size(); i++) {
			String csKey = cs.get(i);
			if(csKey.equalsIgnoreCase(keyCode)) {
				GameLogger.getInstance().log(playerName, "Pressed button " +
						i + " (" + getButtonName(i) + ")");
				if(pressedButtonId.add(new Integer(i))) {
					TargetEvent tEvent = new TargetEvent(this, i, TargetEvent.TYPE.PRESS);
					fireTargetEvent(tEvent);
				}
			}
		}
	}

	@Override
	public void keyReleased(KeyEvent e) {
		if(cs==null) {
			pressedButtonId.clear();
			return;
		}
		String keyCode = KeyEvent.getKeyText(e.getKeyCode());
		for(int i=0; i<cs.size(); i++) {
			String csKey = cs.get(i);
			if(csKey.equalsIgnoreCase(keyCode)) {
				GameLogger.getInstance().log(playerName, "Released button " +
						i + " (" + getButtonName(i) + ")");
				if(pressedButtonId.remove(new Integer(i))) {
					TargetEvent tEvent = new TargetEvent(this, i, TargetEvent.TYPE.RELEASED);
					fireTargetEvent(tEvent);
				}
			}
		}
	}

	private String getButtonName(int i) {
		return gp.getButtonNameById(i);
	}

	@Override
	public void keyTyped(KeyEvent e) {
		// do nothing
		
	}

	public void clearTargets() {
		currentNonHitTargets.clear();
		currentHittedTargets.clear();
		this.state = STATE.READY;
	}

	public void addTarget(Target target) {
		currentNonHitTargets.add(target);
	}

	
	public void addTargetEventListener(TargetEventListener listener) {
		this.listener = listener;
	}
	
	public void removeTargetEventListener(TargetEventListener listener) {
		if(this.listener==listener) {
			listener=null;
		}
	}
	
	private void fireTargetEvent(TargetEvent e) {
		if(listener!=null) {
			listener.targetEventOccurred(e);
		}
	}

	/**
	 * 
	 * @param id target id
	 * @return true if target is activated
	 */
	public boolean hitTarget(int id) {
		boolean isHit = false;
		Iterator<Target> it = currentNonHitTargets.iterator();
		while(it.hasNext()) {
			Target target = it.next();
			if(target.getId() == id) {
				it.remove();
				currentHittedTargets.add(target);
				isHit = true;
				GameLogger.getInstance().log(playerName, "Hit right target " +
						id + " (" + getButtonName(id) + ")");
				if(pushClip==null)
				{
					//Integer note = 
					midiSounds.playRandomNote();
				}
				else
				{
					gp.playClip(pushClip);
				}
				if(currentNonHitTargets.isEmpty()) {
					score++;
					GameLogger.getInstance().log(playerName, "Get all targets. " +
							" Score is now" + score);
					this.state = STATE.FULL_HIT;
					//midiSounds.addNote(note);
				}
			}
		}
		it = currentHittedTargets.iterator();
		while(it.hasNext() && !isHit) {
			Target target = it.next();
			if(target.getId() == id) {
				isHit = true;
				GameLogger.getInstance().log(playerName, "Rehit right target " +
						id + " (" + getButtonName(id) + ")");
				midiSounds.playRepress();
			}
		}
		if(!isHit) {
			GameLogger.getInstance().log(playerName, "Hit wrong target " +
					id + " (" + getButtonName(id) + ")");
			if(!this.isOmitMistakes)
				this.state = STATE.FAIL;
			midiSounds.playWrongPress();
		}
		return isHit;
	}

	public STATE getState() {
		return state;
	}


	public void setPlayerImage(String playerImg) {
		this.playerImg = MediaLoader.createImage(playerImg, MAX_IMAGE_HEIGHT, MAX_IMAGE_WIDTH);
	}
	
	public BufferedImage getPlayerImage() {
		return playerImg;
	}

	public void setPlayedTime(long ms) {
		this.gameTime.setTime(ms);
	}

	public void addPlayedTime(long ms) {
		if(this.state==STATE.READY) {
			this.gameTime.setTime(ms+this.gameTime.getTime());
			currentRoundTime += ms;
		}
	}

	public void setMaxRoundTime(long ms) {
		maxRoundTime = ms;
	}


	public void newRound() {
		currentRoundTime = 0;
	}
	
	public long getCurrentRoundTime()
	{
		return currentRoundTime;
	}
	
	public long getMaxRoundTime()
	{
		return maxRoundTime;
	}


	public GameTime getGameTime() {
		return this.gameTime;
	}

	public String getFolder() {
		return folder;
	}

	public void setFolder(String folder) {
		if(folder!=null && !(folder.endsWith("/") || folder.endsWith("\\")))
		{
			this.folder = folder + "/";
		}
		else
		{
			this.folder = folder;
		}
	}
}
